/* src/interfaces/ecpg/preproc/output.c */

#include "postgres_fe.h"

#include "preproc_extern.h"

static void fc_output_escaped_str(char *fc_cmd, bool fc_quoted);

void output_line_number(void)
{
	char	   *fc_line = hashline_number();

	fprintf(base_yyout, "%s", fc_line);
	free(fc_line);
}

void output_simple_statement(char *fc_stmt, int fc_whenever_mode)
{
	fc_output_escaped_str(fc_stmt, false);
	if (fc_whenever_mode)
		whenever_action(fc_whenever_mode);
	output_line_number();
	free(fc_stmt);
}


/*
 * 在此存储 whenever 动作
 */
struct when when_error,
			when_nf,
			when_warn;

static void fc_print_action(struct when *fc_w)
{
	switch (fc_w->code)
	{
		case W_SQLPRINT:
			fprintf(base_yyout, "sqlprint();");
			break;
		case W_GOTO:
			fprintf(base_yyout, "goto %s;", fc_w->command);
			break;
		case W_DO:
			fprintf(base_yyout, "%s;", fc_w->command);
			break;
		case W_STOP:
			fprintf(base_yyout, "exit (1);");
			break;
		case W_BREAK:
			fprintf(base_yyout, "break;");
			break;
		case W_CONTINUE:
			fprintf(base_yyout, "continue;");
			break;
		default:
			fprintf(base_yyout, "{/* %d 尚未实现 */}", fc_w->code);
			break;
	}
}

void whenever_action(int fc_mode)
{
	if ((fc_mode & 1) == 1 && when_nf.code != W_NOTHING)
	{
		output_line_number();
		fprintf(base_yyout, "\nif (sqlca.sqlcode == ECPG_NOT_FOUND) ");
		fc_print_action(&when_nf);
	}
	if (when_warn.code != W_NOTHING)
	{
		output_line_number();
		fprintf(base_yyout, "\nif (sqlca.sqlwarn[0] == 'W') ");
		fc_print_action(&when_warn);
	}
	if (when_error.code != W_NOTHING)
	{
		output_line_number();
		fprintf(base_yyout, "\nif (sqlca.sqlcode < 0) ");
		fc_print_action(&when_error);
	}

	if ((fc_mode & 2) == 2)
		fputc('}', base_yyout);

	output_line_number();
}

char * hashline_number(void)
{
	/* 如果我们处于调试模式，则不打印行号 */
	if (input_filename
#ifdef YYDEBUG
		&& !base_yydebug
#endif
		)
	{
		/* "* 2" 在这里是为了转义下面的 '\' 和 '"' */
		char	   *fc_line = mm_alloc(strlen("\n#line %d \"%s\"\n") + sizeof(int) * CHAR_BIT * 10 / 3 + strlen(input_filename) * 2);
		char	   *fc_src,
				   *fc_dest;

		sprintf(fc_line, "\n#line %d \"", base_yylineno);
		fc_src = input_filename;
		fc_dest = fc_line + strlen(fc_line);
		while (*fc_src)
		{
			if (*fc_src == '\\' || *fc_src == '"')
				*fc_dest++ = '\\';
			*fc_dest++ = *fc_src++;
		}
		*fc_dest = '\0';
		strcat(fc_dest, "\"\n");

		return fc_line;
	}

	return EMPTY;
}

static char *ecpg_statement_type_name[] = {
	"ECPGst_normal",
	"ECPGst_execute",
	"ECPGst_exec_immediate",
	"ECPGst_prepnormal",
	"ECPGst_prepare",
	"ECPGst_exec_with_exprlist"
};

void output_statement(char *fc_stmt, int fc_whenever_mode, enum ECPG_statement_type fc_st)
{
	fprintf(base_yyout, "{ ECPGdo(__LINE__, %d, %d, %s, %d, ", compat, force_indicator, connection ? connection : "NULL", questionmarks);

	if (fc_st == ECPGst_prepnormal && !auto_prepare)
		fc_st = ECPGst_normal;

	/*
	 * 在以下情况下，stmt 是 CSTRING 或 char_variable。它们必须
	 * 直接输出。 - EXECUTE 的 prepared_name 没有 exprlist -
	 * EXECUTE IMMEDIATE 的 execstring
	 */
	fprintf(base_yyout, "%s, ", ecpg_statement_type_name[fc_st]);
	if (fc_st == ECPGst_execute || fc_st == ECPGst_exec_immediate)
		fprintf(base_yyout, "%s, ", fc_stmt);
	else
	{
		fputs("\"", base_yyout);
		fc_output_escaped_str(fc_stmt, false);
		fputs("\", ", base_yyout);
	}

	/* 将变量转储到 C 文件 */
	dump_variables(argsinsert, 1);
	fputs("ECPGt_EOIT, ", base_yyout);
	dump_variables(argsresult, 1);
	fputs("ECPGt_EORT);", base_yyout);
	reset_variables();

	whenever_action(fc_whenever_mode | 2);
	free(fc_stmt);
}

void output_prepare_statement(char *fc_name, char *fc_stmt)
{
	fprintf(base_yyout, "{ ECPGprepare(__LINE__, %s, %d, ", connection ? connection : "NULL", questionmarks);
	fc_output_escaped_str(fc_name, true);
	fputs(", ", base_yyout);
	fc_output_escaped_str(fc_stmt, true);
	fputs(");", base_yyout);
	whenever_action(2);
	free(fc_name);
}

void output_deallocate_prepare_statement(char *fc_name)
{
	const char *fc_con = connection ? connection : "NULL";

	if (strcmp(fc_name, "all") != 0)
	{
		fprintf(base_yyout, "{ ECPGdeallocate(__LINE__, %d, %s, ", compat, fc_con);
		fc_output_escaped_str(fc_name, true);
		fputs(");", base_yyout);
	}
	else
		fprintf(base_yyout, "{ ECPGdeallocate_all(__LINE__, %d, %s);", compat, fc_con);

	whenever_action(2);
	free(fc_name);
}

static void fc_output_escaped_str(char *fc_str, bool fc_quoted)
{
	int			fc_i = 0;
	int			fc_len = strlen(fc_str);

	if (fc_quoted && fc_str[0] == '"' && fc_str[fc_len - 1] == '"') /* 如果是带引号的字符串
														 * 开头和结尾的引号不进行转义 */
	{
		fc_i = 1;
		fc_len--;
		fputs("\"", base_yyout);
	}

	/* 按字符输出这个，因为我们必须过滤 " 和 \n */
	for (; fc_i < fc_len; fc_i++)
	{
		if (fc_str[fc_i] == '"')
			fputs("\\\"", base_yyout);
		else if (fc_str[fc_i] == '\n')
			fputs("\\\n", base_yyout);
		else if (fc_str[fc_i] == '\\')
		{
			int			fc_j = fc_i;

			/*
			 * 检查这是否是续行，如果是，不要
			 * 输出任何内容，因为换行符已被转义
			 */

			/* 接受 '\' 后的空白，就像其他一些编译器一样 */
			do
			{
				fc_j++;
			} while (fc_str[fc_j] == ' ' || fc_str[fc_j] == '\t');

			if ((fc_str[fc_j] != '\n') && (fc_str[fc_j] != '\r' || fc_str[fc_j + 1] != '\n')) /* 后面不跟
																			 * 换行符 */
				fputs("\\\\", base_yyout);
		}
		else if (fc_str[fc_i] == '\r' && fc_str[fc_i + 1] == '\n')
		{
			fputs("\\\r\n", base_yyout);
			fc_i++;
		}
		else
			fputc(fc_str[fc_i], base_yyout);
	}

	if (fc_quoted && fc_str[0] == '"' && fc_str[fc_len] == '"')
		fputs("\"", base_yyout);
}
